/*
 * ImPduBase.cpp
 *
 *  Created on: 2013-8-27
 *      Author: ziteng@mogujie.com
 */

#include "ImPduBase.h"
#include "ImPduClient.h"

CImPdu::CImPdu()
{
	m_incoming_buf = NULL;
	m_incoming_len = 0;

	m_pdu_header.command_id = 0;
	m_pdu_header.reserved = 0;
}

uchar_t* CImPdu::GetBuffer()
{
	if (m_incoming_buf)
		return m_incoming_buf;
	else
		return m_buf.GetBuffer();
}

uint32_t CImPdu::GetLength()
{
	if (m_incoming_buf)
		return m_incoming_len;
	else
		return m_buf.GetWriteOffset();
}

void CImPdu::WriteHeader()
{
	uchar_t* buf = GetBuffer();

	CByteStream::WriteInt32(buf, GetLength());
	CByteStream::WriteUint16(buf + 4, m_pdu_header.command_id);
	CByteStream::WriteUint16(buf + 6, m_pdu_header.reserved);
}

void CImPdu::SetReserved(uint16_t reserved)
{
	uchar_t* buf = GetBuffer();
	CByteStream::WriteUint16(buf + 6, reserved);
}

int CImPdu::ReadPduHeader(uchar_t* buf, uint32_t len, PduHeader_t* header)
{
	int ret = -1;
	if (len >= IM_PDU_HEADER_LEN && buf && header) {
		CByteStream is(buf, len);

		is >> header->length;
		is >> header->command_id;
		is >> header->reserved;

		ret = 0;
	}

	return ret;
}

CImPdu* CImPdu::ReadPdu(uchar_t *buf, uint32_t len)
{
	uint32_t pdu_len = 0;
	if (!IsPduAvailable(buf, len, pdu_len))
		return NULL;

	uint16_t command_id = CByteStream::ReadUint16(buf + 4);
	CImPdu* pPdu = NULL;

	switch (command_id)
	{
        case IM_PDU_LOGIN_REQ:
            pPdu = new CImPduLoginReq(buf, len);
            break;
        case IM_PDU_LOGIN_RES:
            pPdu = new CImPduLoginRes(buf, len);
            break;
        case IM_PDU_INIT_CALL_REQ:
            pPdu = new CImPduInitCallReq(buf, len);
            break;
        case IM_PDU_INIT_CALL_RES:
            pPdu = new CImPduInitCallRes(buf, len);
            break;
        case IM_PDU_CALL_NOTIFY_REQ:
            pPdu = new CImPduCallNotifyReq(buf, len);
            break;
        case IM_PDU_CALL_NOTIFY_RES:
            pPdu = new CImPduCallNotifyRes(buf, len);
            break;
        case IM_PDU_HUNG_UP_REQ:
            pPdu = new CImPduHungUpReq(buf, len);
            break;
        case IM_PDU_HUNG_UP_RES:
            pPdu = new CImPduHungUpRes(buf, len);
            break;
        case IM_PDU_HUNG_NOTIFY_REQ:
            pPdu = new CImPduHungUpNotifyReq(buf, len);
            break;
        case IM_PDU_HUNG_NOTIFY_RES:
            pPdu = new CImPduHungUpNotifyRes(buf, len);
            break;
        case IM_PDU_ONLINE_USER_REQ:
            pPdu = new CImPduOnlineUserReq(buf, len);
            break;
        case IM_PDU_ONLINE_USER_RES:
            pPdu = new CImPduOnlineUserRes(buf, len);
            break;
	default:
		throw CPduException(command_id, ERROR_CODE_WRONG_SERVICE_ID, "wrong command id");
	}

	pPdu->_SetIncomingLen(pdu_len);
	pPdu->_SetIncomingBuf(buf);
	return pPdu;
}


bool CImPdu::IsPduAvailable(uchar_t* buf, uint32_t len, uint32_t& pdu_len)
{
    if (len < IM_PDU_HEADER_LEN)
		return false;

	pdu_len = CByteStream::ReadUint32(buf);
    
    if (pdu_len < IM_PDU_HEADER_LEN)
        throw CPduException(0, ERROR_CODE_PARSE_FAILED, "wrond pdu length");
    
    if (pdu_len > len)
		return false;
    
	return true;
}

